#include "particleSystem.h"
#include "control.h"
#include "input.h"

ParticleSystem::ParticleSystem(){
name="ParticleSystem";
sceneShaderID="particleColor";
depthShaderID="depthColor";
textureID="NULL";
//menuType.push_back("14MakeGridButton");
bTextured=false;
bUseShader=true;
bFullBright=true;

drawType=DRAW_PARTICLES;

registerProperties();
}

void ParticleSystem::addParticles(int amount){
     for (int i=0;i<amount; i++)
       {
       Particle *p = new Particle;
       p->birth=0.0;
       p->color=Vector4f(1,1,1,1);
       p->normal=Vector3f(0,0,1);
       p->texCoord=Vector3f(1,1,1);
       p->secondaryColor=Vector4f(1,1,1,1);
       particles.push_back(*p);
       }
}

void ParticleSystem::deleteParticle(int particleNumber){


    if (drawType==DRAW_PARTICLES_LINE){
      int mySegment=-1;
      //this gives the starting position of the segment the particle is in
      for (uint i=0; i< segments.size(); i++){
        if ( particleNumber< segments[i]){
            mySegment=i;
            break;
            }
      }

      //delete the particle
      particles.erase(particles.begin()+particleNumber);
      //if the mySegment is not 0 (which would mean that it's the last one)
      // count down the segment start number of all the following ones.
      if (mySegment>-1){
        for (int i=mySegment;i< (int)segments.size(); i++)
            segments[i]--;
        }
     //if we deleted the last particle of the last segment, remove the last segment!
     else if (segments.size()>1){ //leave the last segment!
            if ((int)particles.size()-1==segments[(int)segments.size()-1])
              segments.pop_back();
         }


      //cleaning up the segments
      //compare segment numbers to each other,
      //if their distance is just one, delete the previous one

      for (uint i=0; i< segments.size()-1; i++){
        if (segments[i]+1==segments[i+1] || segments[i]==segments[i+1]){
            segments.erase(segments.begin()+i);
            break;
            }
        }

      //last thing: see if we deleted everything and make sure that we pop the last segment if there are no particles left...

      }
    else
      particles.erase(particles.begin()+particleNumber);

}

void ParticleSystem::addSegment(){

    //always add the first segment
    if (segments.size()==0)
        segments.push_back(particles.size());


    //only add a segment if we actually drew something in it
    if (segments[segments.size()-1] < (int)particles.size()){
            segments.push_back(particles.size());
            cout << "added Line Segment" << endl;
        }


}

void ParticleSystem::addRandomParticles(int amount, float diameter){
 for (int i=0;i<amount;i++)
     {
     Particle p;
     p.location.x=((double)rand()/(double)RAND_MAX) * diameter;
     p.location.y=((double)rand()/(float)RAND_MAX) * diameter;
     p.location.z=((double)rand()/(float)RAND_MAX) * diameter;
     p.location.w=1.0; //scale!!!

     p.normal.x=((double)rand()/(double)RAND_MAX) * diameter;
     p.normal.y=((double)rand()/(float)RAND_MAX) * diameter;
     p.normal.z=((double)rand()/(float)RAND_MAX) * diameter;

     p.color=Vector4f((float)rand()/(float)RAND_MAX,(float)rand()/(float)RAND_MAX,(float)rand()/(float)RAND_MAX,1);
     p.texCoord = Vector3f(p.color.r, p.color.g, p.color.b);
     p.secondaryColor=Vector4f((float)rand()/(float)RAND_MAX,(float)rand()/(float)RAND_MAX,(float)rand()/(float)RAND_MAX,1);
     p.birth=i;
     particles.push_back(p);
     }
}

void ParticleSystem::createSquare(float xStretch, float zStretch){
     int i=0;

     for (int rows=0;rows<sqrt((double)particles.size());rows++)
       {
       for (int columns=0;columns<sqrt((double)particles.size());columns++)
         {
         particles[i].location.x=xStretch*columns;
         particles[i].location.z=zStretch*rows;
         particles[i].normal=Vector3f(0,1,0);
         particles[i].color.r=0.01*columns;
         particles[i].color.g=0.001*rows;
         particles[i].color.b=1;
         particles[i].color.a=1;
         particles[i].texCoord=Vector3f(particles[i].color.r, particles[i].color.g, particles[i].color.b);
         particles[i].birth=rows;
         i++;
         }
       }
}


void ParticleSystem::setup(){
     Actor::setup();
}

void ParticleSystem::update(double deltaTime){
     Actor::update(deltaTime);
     elapsedTime+=deltaTime;
}

void ParticleSystem::updateShaders(){
     Actor::updateShaders();
}


void ParticleSystem::drawParticles(){


    if (bTextured)
        glEnable( GL_POINT_SPRITE_ARB );

    if (particles.size()>0)
      {
      if (drawType==DRAW_PARTICLES_LINE)
        drawAsLine();
      else if (drawType==DRAW_PARTICLES_PATH)
        drawAsPath();
      else
        {
          GLfloat *verts=&particles[0].location.x;
          GLfloat *normals=&particles[0].normal.x;
          GLfloat *colors=&particles[0].color.r;
          GLfloat *secondaryColors=&particles[0].secondaryColor.r;

          glEnableClientState(GL_VERTEX_ARRAY);
          glEnableClientState(GL_NORMAL_ARRAY);
          glEnableClientState(GL_COLOR_ARRAY);
          glEnableClientState(GL_SECONDARY_COLOR_ARRAY);

          glVertexPointer(4, GL_FLOAT,sizeof(particles[0]),verts);
          glNormalPointer(GL_FLOAT,sizeof(particles[0]),normals);
          glColorPointer(4, GL_FLOAT,sizeof(particles[0]),colors);
          glSecondaryColorPointer(4, GL_FLOAT,sizeof(particles[0]),secondaryColors);

          glDrawArrays(GL_POINTS,0,particles.size());

          glDisableClientState(GL_VERTEX_ARRAY);
          glDisableClientState(GL_NORMAL_ARRAY);
          glDisableClientState(GL_COLOR_ARRAY);
          glDisableClientState(GL_SECONDARY_COLOR_ARRAY);
        }
      }

      if (bTextured)
        glDisable(GL_POINT_SPRITE_ARB);

    if (!Control::bRunning)
      {
        glColor4f(color.r,color.g,color.b,color.a);
        glutSolidCube(1/scale.x);
      }

}


void ParticleSystem::drawAsLine(){

  //  cout << "Segment Size: " << segments.size() << endl;

    //glEnable(GL_LINE_SMOOTH);

    if (particles.size()>0)
        glLineWidth(particles[particles.size()-1].location.w *10);

    for (unsigned int i=1; i<segments.size();i++)
        {
        std::vector<Particle> mySegment;
        mySegment.assign(particles.begin()+segments[i-1], particles.begin()+segments[i]-1);

          GLfloat *verts=&particles[0].location.x;
          GLfloat *colors=&particles[0].color.r;
          GLfloat *secondaryColors=&particles[0].secondaryColor.r;

          glEnableClientState(GL_VERTEX_ARRAY);
          glEnableClientState(GL_COLOR_ARRAY);
          glEnableClientState(GL_SECONDARY_COLOR_ARRAY);

          glVertexPointer(4, GL_FLOAT,sizeof(particles[0]),verts);
          glColorPointer(4, GL_FLOAT,sizeof(particles[0]),colors);
          glSecondaryColorPointer(4, GL_FLOAT,sizeof(particles[0]),secondaryColors);

        glDrawArrays(GL_LINE_STRIP,0,mySegment.size());

        glDisableClientState(GL_VERTEX_ARRAY);
        glDisableClientState(GL_COLOR_ARRAY);
        glDisableClientState(GL_SECONDARY_COLOR_ARRAY);
        }

    //draw last segment, or unsegmented line
    std::vector<Particle> mySegment;
    if (segments.size()>0)
        mySegment.assign(particles.begin()+segments[segments.size()-1], particles.end());   //draws last element
    else
        mySegment.assign(particles.begin(), particles.end());   //draws unsegmented system

    //only draw if the segment actually contains something...
          GLfloat *verts=&particles[0].location.x;
          GLfloat *colors=&particles[0].color.r;
          GLfloat *secondaryColors=&particles[0].secondaryColor.r;

          glEnableClientState(GL_VERTEX_ARRAY);
          glEnableClientState(GL_COLOR_ARRAY);
          glEnableClientState(GL_SECONDARY_COLOR_ARRAY);

          glVertexPointer(4, GL_FLOAT,sizeof(particles[0]),verts);
          glColorPointer(4, GL_FLOAT,sizeof(particles[0]),colors);
          glSecondaryColorPointer(4, GL_FLOAT,sizeof(particles[0]),secondaryColors);

    glDrawArrays(GL_LINE_STRIP,0,mySegment.size()-2);   //FIX: doesn't reconnect last element with first one...

    glDisableClientState(GL_VERTEX_ARRAY);
    glDisableClientState(GL_COLOR_ARRAY);
    glDisableClientState(GL_SECONDARY_COLOR_ARRAY);
    //glDisable(GL_LINE_SMOOTH);

}

void ParticleSystem::drawAsPath(){

}

void ParticleSystem::drawCollision(){

    glPushMatrix();
    glLoadMatrixf(baseMatrix);
    renderer->setupShading("color");
    glutSolidCube(1/scale.x);
    glPopMatrix();
}

void ParticleSystem::create(){

renderer->addActor(this);
std::cout << "created a ParticleSystem! \n";
}

TiXmlElement* ParticleSystem::save(TiXmlElement *root){

TiXmlElement * element = Actor::save(root);
for (unsigned int i=0;i<particles.size();i++)
    {
    TiXmlElement * pElement = new TiXmlElement("particle");

        TiXmlElement * mElement = new TiXmlElement("location");
        char loc[50];
        sprintf(loc,"vec4f %f %f %f %f", particles[i].location.x, particles[i].location.y, particles[i].location.z, particles[i].location.w);
        mElement->LinkEndChild(new TiXmlText(loc));
        pElement->LinkEndChild( mElement );

        mElement = new TiXmlElement("color");
        char col[50];
        sprintf(col,"vec4f %f %f %f %f", particles[i].color.r, particles[i].color.g, particles[i].color.b, particles[i].color.a);
        mElement->LinkEndChild(new TiXmlText(col));
        pElement->LinkEndChild( mElement );

        mElement = new TiXmlElement("secondaryColor");
        char secCol[50];
        sprintf(secCol,"vec4f %f %f %f %f", particles[i].secondaryColor.r, particles[i].secondaryColor.g, particles[i].secondaryColor.b, particles[i].secondaryColor.a);
        mElement->LinkEndChild(new TiXmlText(secCol));
        pElement->LinkEndChild( mElement );

        mElement = new TiXmlElement("birth");
        char bir[50];
        sprintf(bir,"float %f", particles[i].birth);
        mElement->LinkEndChild(new TiXmlText(bir));
        pElement->LinkEndChild( mElement );

    element->LinkEndChild( pElement );
    }

TiXmlElement * sElement = new TiXmlElement("segment");
    for (unsigned int i=0;i<segments.size();i++)
        {
        TiXmlElement * mElement = new TiXmlElement("segmentLocation");
        char loc[50];
        sprintf(loc,"int %i", segments[i]);
        mElement->LinkEndChild(new TiXmlText(loc));
        sElement->LinkEndChild( mElement );
        }
element->LinkEndChild( sElement );

return element;
}

void ParticleSystem::load(TiXmlElement *myInfo){

Actor::load(myInfo);

TiXmlElement * element;
element=myInfo->FirstChildElement( "particle" );

    for( ; element!=NULL; element=element->NextSiblingElement())
	{
      addParticles(1);
      TiXmlElement * property=NULL;
      property=element->FirstChildElement("location");

      if (property){
        particles[particles.size()-1].location=readVector4f((char*)property->GetText());
        }

      property=NULL;
      property=element->FirstChildElement("color");

      if (property)
        particles[particles.size()-1].color=readVector4f((char*)property->GetText());


      property=NULL;
      property=element->FirstChildElement("secondaryColor");

      if (property)
        particles[particles.size()-1].secondaryColor=readVector4f((char*)property->GetText());

      property=NULL;
      property=element->FirstChildElement("birth");

      if (property)
        particles[particles.size()-1].birth=readFloat((char*)property->GetText());
    }

element=myInfo->FirstChildElement( "segment" );

    if (element){
        element= element->FirstChildElement("segmentLocation");
        for( ; element!=NULL; element=element->NextSiblingElement())
        {
            cout << "loading segment..." << endl;
            segments.push_back(readInt((char*)element->GetText()));
        }
    }
}



/*
      for (unsigned int i=0;i<segments.size();i++){
        if (particleNumber<segments[i]){
            mySegment=i;
            break;
            }
        //or, if we are deleting from the last segment...
        else{
            cout << "deleting from the last segment!" << endl;
            mySegment=segments.size()-1;
            }
        }
        //make sure we draw one particle less in every segment above the one we delete from
        for (unsigned int i=mySegment;i<segments.size();i++){
          //dont delete from the last segment
          if (mySegment<(int)segments.size()-1)
            //segments[i]--;
          //check for unnecessary segments
          if (i>0)
            if (segments[i]==segments[i-1])
              {
              segments.erase(segments.begin()+i-1);
              i--;
              }
        }
        particles.erase(particles.begin()+particleNumber);
        //particles[particleNumber].color=Vector4f(1,0,0,1);

*/
